package com.blb.registry;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 自定义Eureka服务器
 */
public class MyEurekaServer {

    public static final int PORT = 8888;
    public static final int HEARTBEAT_ALIVE_TIME = 90000;
    public static final String COMMAND_REGISTRY = "registry";
    public static final String COMMAND_RENEW = "renew";
    public static final String COMMAND_SEARCH = "search";
    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");

    //服务地址map
    private Map<String,String> addressMap = new HashMap<>();
    //服务心跳map
    private Map<String,String> heartbeatMap = new HashMap<>();

    public void start(){
        //定时检查心跳
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
        pool.scheduleAtFixedRate(() -> {
            checkHeartbeat();
        },10,10, TimeUnit.SECONDS);
        //处理客户端
        try(ServerSocket serverSocket = new ServerSocket(PORT)){
            while(true){
                Socket client = serverSocket.accept();
                handleClient(client);
            }
        }catch (IOException ex){
            ex.printStackTrace();
        }
    }

    /**
     * 处理客户端消息
     * @param client
     */
    private void handleClient(Socket client){
        try(
            ObjectOutputStream outputStream = new ObjectOutputStream(client.getOutputStream());
            ObjectInputStream inputStream = new ObjectInputStream(client.getInputStream());) {
            String info = inputStream.readUTF();
            //注册消息格式：registry name ip port
            //心跳消息格式：renew name ip port
            //查询消息格式：search name
            String[] strings = info.split(" ");
            if(COMMAND_REGISTRY.equals(strings[0])){
                System.out.println("收到客户端注册信息:" + info);
                //保存服务名和地址
                addressMap.put(strings[1],strings[2] + ":" + strings[3]);
                //保存服务名和心跳时间
                heartbeatMap.put(strings[1], LocalDateTime.now().format(FORMATTER));
                outputStream.writeUTF("success");
            }else if(COMMAND_RENEW.equals(strings[0])){
                System.out.println("收到客户端心跳包:" + info);
                //更新服务名和地址
                addressMap.put(strings[1],strings[2] + ":" + strings[3]);
                //更新服务名和心跳时间
                heartbeatMap.put(strings[1], LocalDateTime.now().format(FORMATTER));
                outputStream.writeUTF("success");
            }else if(COMMAND_SEARCH.equals(strings[0])){
                System.out.println("收到客户端搜索请求：" + info);
                //查询服务的地址
                String address = addressMap.get(strings[1]);
                outputStream.writeUTF(address);
            }
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查心跳
     */
    private void checkHeartbeat(){
        Set<String> keys = heartbeatMap.keySet();
        for(String key : keys){
            //取得每个服务上次的心跳时间，离当前时间超过最大值，从集合中删除
            String time = heartbeatMap.get(key);
            LocalDateTime lastHeartbeat = LocalDateTime.parse(time, FORMATTER);
            Duration duration = Duration.between(lastHeartbeat, LocalDateTime.now());
            if(duration.toMillis() > HEARTBEAT_ALIVE_TIME){
                System.out.println(key + "长时间没有心跳，从服务清单剔除");
                heartbeatMap.remove(key);
                addressMap.remove(key);
            }
        }
    }

    public static void main(String[] args) {
        new MyEurekaServer().start();
    }
}
